//
// Created by martin on 2/5/22.
//

#ifndef MUDUO_MUTEX_H
#define MUDUO_MUTEX_H

#include "muduo/base/CurrentThread.h"
#include "muduo/base/noncopyable.h"

#include <assert.h>
#include <pthread.h>
#include <errno.h>
#include <sys/cdefs.h> // for __BEGIN_DECLS


// Thread safely annotations {
// Enable thread safety attributes only with clang.

// The attributes can be safely erased when compiling with other compilers.
// Clang compiler: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
// SWIG compiler: http://www.swig.org/Doc3.0/CPlusPlus11.html
#if defined(__clang__) && (!defined(SWIG))
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif

#define CAPABILITY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(capability(x))

#define SCOPED_CAPABILITY \
  THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)

#define GUARDED_BY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))

#define PT_GUARDED_BY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))

#define ACQUIRED_BEFORE(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))

#define ACQUIRED_AFTER(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))

#define REQUIRES(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))

#define REQUIRES_SHARED(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))

#define ACQUIRE(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))

#define ACQUIRE_SHARED(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))

#define RELEASE(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))

#define RELEASE_SHARED(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))

#define TRY_ACQUIRE(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))

#define TRY_ACQUIRE_SHARED(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))

#define EXCLUDES(...) \
  THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))

#define ASSERT_CAPABILITY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))

#define ASSERT_SHARED_CAPABILITY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))

#define RETURN_CAPABILITY(x) \
  THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))

#define NO_THREAD_SAFETY_ANALYSIS \
  THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)

// } End of Thread safely annotations


#ifdef CHECK_PTHREAD_RETURN_VALUE
#ifdef NDEBUG
__BEGIN_DECLS
extern void __assert_perror_fail (int errnum,
                                  const char* file,
                                  unsigned int line,
                                  const char* function)
  noexcept __attribute__ ((__noreturn__));
__END_DECLS
#endif // NDEBUG

#define MCHECK(ret) ({                                           \
__typeof__ (ret) errnum = (ret);                                 \
if (__builtin_expect(errnum != 0, 0))                            \
    __assert_perror_fail (errnum, __FILE__, __LINE__, __func__); \
})
#else // CHECK_PTHREAD_RETURN_VALUE
#define MCHECK(ret) ({           \
__typeof__ (ret) errnum = (ret); \
assert(errnum == 0);             \
(void) errnum;                   \
})

#endif // CHECK_PTHREAD_RETURN_VALUE


namespace muduo
{

class CAPABILITY("mutex") MutexLock : public noncopyable
{
public:
    MutexLock()
    : holder_(0)
    {
        MCHECK(pthread_mutex_init(&mutex_, NULL));
    }
    ~MutexLock()
    {
        assert(holder_ == 0);
        MCHECK(pthread_mutex_destroy(&mutex_));
    }

    // must be called when locked, i.e. for assertion
    bool isLockedByThisThread() const
    {
        return holder_ == CurrentThread::tid();
    }

    void assertLocked() const ASSERT_CAPABILITY(this)
    {
        assert(isLockedByThisThread());
    }

    // internal usage

    /**
     * lock mutex_ and check lock status.
     * assign current thread's tid as the lock's holder
     */
    void lock() ACQUIRE()
    {
        MCHECK(pthread_mutex_lock(&mutex_));
        assignHolder();
    }

    /**
     * unassign the lock's holder, then unlock mutex_.
     */
    void unlock() RELEASE()
    {
        unassignHolder();
        MCHECK(pthread_mutex_unlock(&mutex_));
    }

    // non-const, because return a pointer to class's (private) data
    pthread_mutex_t* getPthreadMutex()
    {
        return &mutex_;
    }

private:
    friend class Condition;

    /* give up the MutexLock */
    class UnassignGuard : public noncopyable
    {
    public:
        explicit UnassignGuard(MutexLock& owner)
        : owner_(owner)
        {
            owner_.unassignHolder(); // clear lock owner_ holder
        }
        ~UnassignGuard()
        {
            owner_.assignHolder();  // set current thread as the lock holder
        }

    private:
        MutexLock& owner_;
    };

    /**
     * set current thread as the lock holder
     * by assigning current thread's tid to holder
     */
    void assignHolder()
    {
        holder_ = CurrentThread::tid();
    }

    /**
     * clear the lock holder
     */
    void unassignHolder()
    {
        holder_ = 0;
    }

    pthread_mutex_t mutex_;
    pid_t holder_;
};

class SCOPED_CAPABILITY MutexLockGuard : public noncopyable
{
public:
    explicit MutexLockGuard(MutexLock& mutex) ACQUIRE(mutex)
    : mutex_(mutex)
    {
        mutex_.lock();
    }
    ~MutexLockGuard() RELEASE()
    {
        mutex_.unlock();
    }

private:
    MutexLock& mutex_;
};

} // namespace muduo

/*
 * Prevent misuse like:
 * MutexLockGuard(mutex_);
 * A tempory object doesn't hold the lock for long.
 */
#define MutexLockGuard(x) error "Missing guard object name"

#endif //MUDUO_MUTEX_H
